/**
  ******************************************************************************
  * @file    memory.c
  * @author  ashuai0110
  * @version V2.3
  * @date    2024-07-01
  * @brief   内存管理(rt-thread的mem,小内存管理算法<2MB)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2023-10-27    ashuai0110    完成基本内容
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "memory.h"

/**
 * @addtogroup modules
 * @{
 */

/**
 * @defgroup memory memory
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup memory_local_macros memory local macros
 * @{
 */
 
/**
 * @defgroup memory_check_parameters_validity memory check parameters validity
 * @{
 */
/* 内存管理内存申请大小有效性检查 */
#define IS_VALID_ALLOC_SIZE(x)                                                \
(       (x) != 0u)
/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/
/**
 * @defgroup memory_local_variables memory local variables
 * @{
 */

static char memory[MEM_SIZE] __attribute((aligned(MEM_ALIGN_SIZE))); /*!< 管理的内存空间 */
static mem_t *tailMem; /*!< 末尾管理者 */
static mem_t *freeMem; /*!< 空闲管理者 */
#if _MEM_STATES
static uint32_t memUsedSize; /*!< 内存已使用大小 */
#endif

/**
 * @}
 */

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/
/**
 * @defgroup memory_local_functions memory local functions
 * @{
 */

/**
  * @brief  检查合并前后管理者管理的内存块
  *
  * @param  _mem : 管理者
  *
  * @retval None
  */
static void mem_check_free(mem_t *_mem)
{
    mem_t *nmem;
    mem_t *pmem;
    
    /* 检查下一个管理者属性 */
    nmem = (mem_t *)&memory[_mem->next];
    /* 管理空间未使用且不是末尾管理者 */
    if(nmem != _mem && !nmem->used && nmem != tailMem)
    {
        if(freeMem == nmem) { freeMem = _mem; }
        /* 合并内存块 */
        _mem->next = nmem->next;
        ((mem_t *)&memory[nmem->next])->prev = (char *)_mem - memory;
#if _MEM_STATES
        memUsedSize -= sizeof(mem_t);
#endif
        mem_set((char *)nmem, 0, sizeof(mem_t));
    }
    /* 检查上一个管理者属性 */
    pmem = (mem_t *)&memory[_mem->prev];
    /* 管理空间未使用 */
    if(pmem != _mem && !pmem->used)
    {
        if(freeMem == _mem) { freeMem = pmem; }
        /* 合并内存块 */
        pmem->next = _mem->next;
        ((mem_t *)&memory[_mem->next])->prev = (char *)pmem - memory;
#if _MEM_STATES
        memUsedSize -= sizeof(mem_t);
#endif
        mem_set((char *)_mem, 0, sizeof(mem_t));
    }
}

/**
 * @}
 */

/**
 * @defgroup memory_global_functions memory global functions
 * @{
 */

/**
  * @brief  内存管理初始化
  *
  * @param  None
  *
  * @retval None
  */
void mem_init(void)
{
    mem_t *mem;
    
    /* 初始化头部管理者/空闲管理者 */
    mem = (mem_t *)memory;
    mem->code = MEM_CODE;
    mem->used = 0;
    mem->next = MEM_SIZE - sizeof(mem_t);
    mem->prev = 0;
    /* 初始化末尾管理者 */
    tailMem = (mem_t *)&memory[mem->next];
    tailMem->code = MEM_CODE;
    tailMem->used = 1;
    tailMem->next = 0;
    tailMem->prev = 0;
    
    freeMem = mem;
#if _MEM_STATES
    memUsedSize = sizeof(mem_t) * 2;
#endif
}

/**
  * @brief  内存分配
  *
  * @param  size : 需分配的内存大小byte
  *
  * @retval NULL:空指针  !NULL:分配的内存地址
  */
void *mem_alloc(uint32_t size)
{
    uint32_t offset, offset2;
    mem_t *mem, *mem2;
    
    ASSERT_PARAM(IS_VALID_ALLOC_SIZE(size));
    
    /* 字节向上对齐 */
    size = ALIGN_UP(size, MEM_ALIGN_SIZE);
    /* 若申请内存超出可用内存则返回NULL */
    if(size > (MEM_SIZE - sizeof(mem_t) * 2)) { return NULL; }
    
    /* 轮询查找管理者 */
    for(offset = (char *)freeMem - memory; \
        offset < MEM_SIZE - sizeof(mem_t) * 2 - size; \
        offset = ((mem_t *)&memory[offset])->next)
    {
        mem = (mem_t *)&memory[offset]; /* 找到管理者 */
        /* 管理者管理的内存未使用且大于等于申请大小 */
        if(!mem->used && (mem->next - offset - sizeof(mem_t)) >= size)
        {
            /* 管理者管理的内存减去申请掉的还可以容纳一个管理者和最小空间 */
            if((mem->next - offset - sizeof(mem_t)) >= (size + sizeof(mem_t) + MEM_ALIGN_SIZE))
            {
                offset2 = offset + size + sizeof(mem_t); /* 下一个管理者的偏移量 */
                /* 创建下一个管理者mem2 */
                mem2 = (mem_t *)&memory[offset2];
                mem2->code = MEM_CODE;
                mem2->used = 0;
                mem2->next = mem->next; /* 继承当前管理者mem的next */
                mem2->prev = offset; /* prev记录当前管理者mem的偏移量 */
                /* 当前管理者mem记录下一个管理者mem2的偏移 */
                mem->next = offset2;
                /* 若下一个管理者mem2的next不是末尾管理者的偏移则说明后面还有一个非末尾的管理者 */
                if(mem2->next != (MEM_SIZE - sizeof(mem_t)))
                {
                    ((mem_t *)&memory[mem2->next])->prev = offset2; /* 后面非末尾的管理者的prev记录mem2的偏移量 */
                }
#if _MEM_STATES
                memUsedSize += (size + sizeof(mem_t));
#endif
            }
            else
            {
#if _MEM_STATES
                memUsedSize += mem->next - ((char *)mem - memory);
#endif
            }
            /* 当前管理者标记已使用并赋值特殊标记 */
            mem->used = 1;
            mem->code = MEM_CODE;
            /* 寻找并更新空闲管理者 */
            if(mem == freeMem)
            {
                while(freeMem->used && freeMem != tailMem)
                {
                    freeMem = (mem_t *)&memory[freeMem->next];
                }
            }
            mem_set((void *)((char *)mem + sizeof(mem_t)), 0, size); /* 清理申请到的内存 */
            
            return (void *)((char *)mem + sizeof(mem_t));
        }
    }

    return NULL;
}

/**
  * @brief  内存重新分配
  *
  * @param  pStr : 需重新分配的内存地址
  *
  * @param  size : 需重新分配的内存大小
  *
  * @retval NULL:空指针  !NULL:分配的内存地址
  */
void *mem_realloc(void *pStr, uint32_t size)
{
    uint32_t offset, offset2, oldsize;
    mem_t *mem, *mem2;
    void *pNewStr = pStr;
    
    /* 字节向上整数对齐 */
    size = ALIGN_UP(size, MEM_ALIGN_SIZE);
    /* 若申请大小超出最大可申请大小则返回NULL */
    if(size > (MEM_SIZE - sizeof(mem_t) * 2))
    {
        return NULL;
    } /* 若申请大小为0则释放该内存并返回NULL */
    else if(0 == size)
    {
        mem_free(pStr);
        return NULL;
    }
    /* 若要重新申请的内存本身为空 则使用mem_alloc为其申请内存 */
    if(pStr == NULL) { return mem_alloc(size); }
    /* 若要重新申请的内存不在内存管理的范围 则返回其自身无法重新申请 */
    if((char *)pStr < memory || (char *)pStr >= (char *)(memory + MEM_SIZE)) { return pStr; }
    
    mem = (mem_t *)((char *)pStr - sizeof(mem_t)); /* 管理者的地址 */
    offset = (char *)mem - memory; /* 管理者的偏移 */
    oldsize = mem->next - offset - sizeof(mem_t); /* 原本的内存大小(下一个管理者的偏移-此内存管理者的偏移-管理者大小) */
    if(oldsize == size) { return pStr; } /* 若重新申请的内存大小等于原本的内存大小 则返回其自身不重新申请 */
    /* 新分配大小+一个管理者+最小空间小于原有大小 */
    if(oldsize > (size + sizeof(mem_t) + MEM_ALIGN_SIZE))
    {
        /* 下一个管理者的偏移量 */
        offset2 = offset + size + sizeof(mem_t);
        /* 创建下一个管理者mem2 */
        mem2 = (mem_t *)&memory[offset2];
        mem2->code = MEM_CODE;
        mem2->used = 0;
        mem2->next = mem->next; /* 继承当前管理者mem的next */
        mem2->prev = offset; /* prev记录当前管理者mem的偏移量 */
        /* 当前管理者mem记录下一个管理者mem2的偏移 */
        mem->next = offset2;
        /* 若下一个管理者mem2的next不是末尾管理者的偏移则说明后面还有一个非末尾的管理者 */
        if(mem2->next != (MEM_SIZE - sizeof(mem_t)))
        {
            ((mem_t *)&memory[mem2->next])->prev = offset2; /* 后面非末尾的管理者的prev记录mem2的偏移量 */
        }
#if _MEM_STATES
        memUsedSize -= (oldsize - size - sizeof(mem_t));
#endif
        if(mem2 < freeMem) { freeMem = mem2; }
        mem_set((void *)((char *)mem2 + sizeof(mem_t)), 0, oldsize - size - sizeof(mem_t));
        /* 检查前后管理者管理的块是否未使用,将其合并 */
        mem_check_free(mem2);
    }
    else
    {
        /* 新分配一个内存空间拷贝数据并释放旧内存空间 */
        pNewStr = mem_alloc(size);
        if(pNewStr != NULL)
        {
            mem_copy(pNewStr, pStr, oldsize < size ? oldsize : size);
            mem_free(pStr);
        }
    }
    
    return pNewStr;
}

/**
  * @brief  内存释放
  *
  * @param  pStr : 需释放的内存地址
  *
  * @retval None
  */
void mem_free(void *pStr)
{
    mem_t *mem;
    
    ASSERT_PARAM(IS_VALID_POINT(pStr));
    /* 检查此内存地址是否是MEM_ALIGN_SIZE字节对齐 不是则退出 */
    if(((uint32_t)pStr & (MEM_ALIGN_SIZE - 1)) != 0) { return ; }
    /* 若此内存地址不在内存管理的范围 则退出 */
    if((char *)pStr < memory || (char *)pStr >= (char *)(memory + MEM_SIZE)) { return ; }
    
    mem = (mem_t *)((char *)pStr - sizeof(mem_t)); /* 管理者的地址 */
    /* 若此内存未使用或内存标志不对 则退出 */
    if(!mem->used || mem->code != MEM_CODE) { return ; }
    /* 标记此内存地址未使用 */
    mem->used = 0;
    mem->code = MEM_CODE;
#if _MEM_STATES
    memUsedSize -= (mem->next - ((char *)pStr - memory));
#endif
    if(mem < freeMem) { freeMem = mem; } /* 若此内存管理者小于空闲管理者则更改空闲管理者地址 */
    mem_set(pStr, 0, mem->next - ((char *)pStr - memory)); /* 清理内存数据 */
    /* 检查前后管理者管理的块是否未使用,将其合并 */
    mem_check_free(mem);
}

/**
  * @brief  批量填充内存值
  * 
  * @param  pStr : 需填充的内存地址
  * 
  * @param  c    : 填充值
  * 
  * @param  n    : 填充数量byte
  * 
  * @retval 返回输入参数pStr
  */
void *mem_set(void *pStr, int c, uint32_t n)
{
    char *s = (char *)pStr;

    /* 循环设置填充值 */
    while(n--) { *s++ = c; }

    return pStr;
}

/**
  * @brief  批量拷贝内存值
  * 
  * @param  dest : 需粘贴的目标内存地址
  * 
  * @param  src  : 需拷贝的源内存地址
  * 
  * @param  n    : 拷贝数量byte
  * 
  * @retval 返回输入参数dest
  */
void *mem_copy(void *dest, const void *src, uint32_t n)
{
    char *d = (char *)dest, *s = (char *)src;
    uint32_t len;

    /* 若目标地址小于等于源地址或大于源地址加上拷贝数量则说明目标地址不在源地址以及后续数据内 */
    if(d <= s || d > (s + n))
    {
        /* 此时从前往后拷贝即可 */
        while(n--) { *d++ = *s++; }
    } /* 否则说明目标地址在源地址以及后续数据内(和源地址以及后续数据的内存重叠) */
    else
    {
        /* 此时从后往前拷贝 避免从前往后拷贝时后续数据被修改 */
        for(len = n; len > 0; len--)
        {
            d[len - 1] = s[len - 1];
        }
    }

    return dest;
}

#if _MEM_STATES
/**
  * @brief  获取内存总量
  * 
  * @param  None
  * 
  * @retval 返回内存总量
  */
uint32_t mem_get_total_size(void)
{
    return MEM_SIZE;
}

/**
  * @brief  获取内存使用量
  * 
  * @param  None
  * 
  * @retval 返回内存使用量
  */
uint32_t mem_get_used_size(void)
{
    return memUsedSize;
}
#endif

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
